package dao

import (
	"TaskAssignmentSystem/pkg/db/mysql"
	"fmt"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"gorm.io/gorm/schema"
	"reflect"
	"strings"
	"time"
)

var _db = mysql.GetDB()

type Page[T any] struct {
	Page        int64     `json:"page"`
	PageSize    int64     `json:"page_size"`
	Order       string    `json:"order"`
	Distinct    string    `json:"distinct"`
	Total       int64     `json:"total"`
	Data        []T       `json:"data"`
	WhereStr    []string  `json:"where_str"`
	LeftJoin    *[]string `json:"left_join"`
	SelectField *string   `json:"select_field"`
	Group       string    `json:"group"`
}

type PageResponse[T any] struct {
	Page     int64 `json:"page"`
	PageSize int64 `json:"page_size"`
	Total    int64 `json:"total"`
	Pages    int64 `json:"pages"` // 总页数
	Data     []T   `json:"data"`
}

// 将原始的Page结构体转换为前端需要的PageResponse结构体

func NewPageResponse[T any](page *Page[T]) *PageResponse[T] {
	return &PageResponse[T]{
		Page:     page.Page,
		PageSize: page.PageSize,
		Total:    page.Total,
		// 通常要将Data中的元素进行转换，比如拼接名字、时间格式，但这里没有要转换的字段
		Data: page.Data,
	}
}

func (page *Page[T]) SelectPage(wrapper map[string]interface{}) (e error) {
	e = nil
	var model T
	var tx *gorm.DB

	tx = _db.Model(&model).Preload(clause.Associations).Where(wrapper)
	//tx = _db.Model(&model).Where(wrapper)
	if page.Distinct != "" {
		tx = tx.Distinct(page.Distinct)
	}
	tx = tx.Preload(clause.Associations)
	for _, s := range page.WhereStr {
		if s == "deleted_at is not Null" {
			tx = tx.Where(s).Unscoped()
		}
		tx = tx.Where(s)
	}
	//left join
	if page.LeftJoin != nil {
		if page.SelectField != nil {
			tx = tx.Select(*page.SelectField)
		} else {
			tx = tx.Select("*")
		}
		for _, s := range *page.LeftJoin {
			tx = tx.Joins(s)
		}

	}
	tx.Count(&page.Total)
	if page.Total == 0 {
		// 没有符合条件的数据，直接返回一个T类型的空列表
		page.Data = []T{}
		return
	}
	if page.Order != "" {
		tx = tx.Order(page.Order)
	}
	if page.Group != "" {
		tx = tx.Group(page.Group)
	}
	// 查询结果可以直接存到Page的Data字段中，因为编译的时候page.Data是有确定类型的

	e = tx.Scopes(Paginate(page)).Find(&page.Data).Error
	return
}

func (page *Page[T]) SelectPage1(wrapper map[string]interface{}) (e error) {
	e = nil
	var model T
	var tx *gorm.DB

	//tx = _db.Model(&model).Preload(clause.Associations).Where(wrapper)
	tx = _db.Model(&model).Where(wrapper)
	if page.Distinct != "" {
		tx = tx.Distinct(page.Distinct)
	}
	//tx = tx.Preload(clause.Associations)
	for _, s := range page.WhereStr {
		if s == "deleted_at is not Null" {
			tx = tx.Where(s).Unscoped()
		}
		tx = tx.Where(s)
	}
	//left join
	if page.LeftJoin != nil {
		if page.SelectField != nil {
			tx = tx.Select(*page.SelectField)
		} else {
			tx = tx.Select("*")
		}
		for _, s := range *page.LeftJoin {
			tx = tx.Joins(s)
		}

	}
	tx.Count(&page.Total)
	if page.Total == 0 {
		// 没有符合条件的数据，直接返回一个T类型的空列表
		page.Data = []T{}
		return
	}
	if page.Order != "" {
		tx = tx.Order(page.Order)
	}
	// 查询结果可以直接存到Page的Data字段中，因为编译的时候page.Data是有确定类型的

	e = tx.Scopes(Paginate(page)).Find(&page.Data).Error
	return
}

// Paginate加上T就行

func Paginate[T any](page *Page[T]) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		if page.Page <= 0 {
			page.Page = 0
		}
		switch {
		case page.PageSize > 100000:
			page.PageSize = 100000
		case page.PageSize == 0:
			page.PageSize = 10
		}
		pages := page.Total / page.PageSize
		if page.Total%page.PageSize != 0 {
			pages++
		}
		p := page.Page
		size := page.PageSize
		offset := int((p - 1) * size)
		if page.PageSize == -1 {
			//fmt.Println(page)
			return db
		} else {
			return db.Offset(offset).Limit(int(size))
		}

	}
}

type SelCount[T any] struct {
	Count int64 `json:"count"`
}

func (s *SelCount[T]) SelectCount(where map[string]interface{}) {
	var model T
	_db.Model(&model).Where(where).Count(&s.Count)
	return
}

type Common[T any] struct {
	Tx          *gorm.DB
	Query       map[string]interface{}
	CustomWhere []string
	Field       *string
	LeftJoin    []string `json:"left_join"`
}

func (c *Common[T]) model() *gorm.DB {
	var model T
	var tx *gorm.DB
	if c.Tx != nil {
		tx = c.Tx
	} else {
		tx = _db
	}
	return tx.Model(&model)
}

func (c *Common[T]) unscoped() *gorm.DB {
	tx := c.model()
	return tx.Unscoped()
}

func (c *Common[T]) RemoveByWhere(where map[string]interface{}) *gorm.DB {
	var model T
	return c.model().Where(where).Delete(&model)
}

func (c *Common[T]) RemoveUnscopedWhere(where map[string]interface{}) *gorm.DB {
	var model T
	tx := c.unscoped().Where(where)
	for _, s := range c.CustomWhere {
		tx = tx.Where(s)
	}
	return tx.Delete(&model)
}

func (c *Common[T]) RemoveUnscopedPreload(where map[string]interface{}) *gorm.DB {
	var model T
	tx := c.unscoped().Where(where)
	for _, s := range c.CustomWhere {
		tx = tx.Where(s)
	}
	return tx.Delete(&model)
}

func (c *Common[T]) Create(created []T) *gorm.DB {
	return c.model().Create(&created)
}

func (c *Common[T]) CreateT(created []*T) *gorm.DB {
	return c.model().Create(&created)
}

func (c *Common[T]) Count(query map[string]interface{}) (count int64, err error) {
	tx := c.model()
	tx = tx.Where(query)
	for _, s := range c.CustomWhere {
		tx = tx.Where(s)
	}
	err = tx.Count(&count).Error
	return
}

func (c *Common[T]) Together() (res T, err error) {
	tx := c.model()
	if c.Field != nil {
		tx = tx.Select(*c.Field)
	}
	for _, s := range c.CustomWhere {
		tx = tx.Where(s)
	}
	tx.Where(c.Query).Scan(&res)
	return
}

func (c *Common[T]) UnscopedCount(query map[string]interface{}) (count int64, err error) {
	tx := c.model()
	tx = tx.Unscoped().Where(query)
	for _, s := range c.CustomWhere {
		tx = tx.Where(s)
	}
	err = tx.Count(&count).Error
	return
}

func (c *Common[T]) CreateOne(created T) *gorm.DB {
	tx := c.model()
	res := tx.Create(&created)

	return res
}

func (c *Common[T]) CreateOneReturnId(created *T) *gorm.DB {
	tx := c.model()
	res := tx.Create(created)
	return res
}

func (c *Common[T]) DeleteById(id uint) *gorm.DB {
	var model T
	tx := c.model()
	return tx.Unscoped().Where("id = ?", id).Delete(&model)
}

func (c *Common[T]) Truncate(table string) *gorm.DB {
	tx := c.model()
	return tx.Exec("TRUNCATE TABLE " + table)
}

func (c *Common[T]) ExecSql(sql string) *gorm.DB {
	tx := c.model()
	return tx.Exec(sql)
}

func (c *Common[T]) UpdateOne(id uint, set map[string]interface{}) *gorm.DB {
	tx := c.model()
	return tx.Where("id = ?", id).Updates(set)
}

func (c *Common[T]) Update(set map[string]interface{}) *gorm.DB {
	tx := c.model()
	for _, s := range c.CustomWhere {
		tx.Where(s)
	}
	return tx.Where(c.Query).Updates(set)
}

func (c *Common[T]) BatchUpdate(ids []uint, set map[string]interface{}) *gorm.DB {
	tx := c.model()
	return tx.Where("id IN ?", ids).Updates(set)
}

func (c *Common[T]) BatchUpdateWithWhere(query, set map[string]interface{}) *gorm.DB {
	tx := c.model()
	return tx.Where(query).Updates(set)
}

func (c *Common[T]) BatchUpdateWithWhereForObj(query map[string]interface{}, set T) *gorm.DB {
	tx := c.model()
	return tx.Where(query).Updates(&set)
}

func (c *Common[T]) First(query map[string]interface{}) (T, error) {
	var model T
	tx := c.model()
	for _, w := range c.CustomWhere {
		tx = tx.Where(w)
	}
	err := tx.Where(query).Preload(clause.Associations).First(&model).Error
	return model, err
}

func (c *Common[T]) Save(obj T) *gorm.DB {
	tx := c.model()
	tx.Where(c.Query)
	return tx.Save(&obj)
}

// 新增或者覆盖更新
// obj 模型对象
// uniqueKeyFieldName 唯一性字段，对象中的字段名，覆盖更新的依据
func (c *Common[T]) CreateOrUpdate(obj T, uniqueKeyFieldName string) *gorm.DB {
	tx := c.model()
	var upParam = make(map[string]interface{})
	var query = make(map[string]interface{})
	//反射解析对象
	immutable := reflect.ValueOf(obj)
	//gorm解析对象，获取表名和表字段等信息
	if err := tx.Statement.Parse(obj); err == nil && tx.Statement.Schema != nil {
		for _, f := range tx.Statement.Schema.Fields {
			// Id，CreatedAt，DeletedAt 这三个字段不处理；新增时Save方法内已处理，更新时只更新UpdatedAt
			if strings.EqualFold(f.Name, "Id") || strings.EqualFold(f.Name, "CreatedAt") || strings.EqualFold(f.Name, "DeletedAt") {
				continue
			}
			//通过反射解析对象内字段名字，获取值
			v := immutable.FieldByName(f.Name)
			//设置唯一键，查询时用
			if strings.EqualFold(f.Name, uniqueKeyFieldName) {
				c.setMap(query, f, &v)
			}
			//设置更新的map
			c.setMap(upParam, f, &v)
		}
	}

	//查询是否存在
	tx.Where(query)
	var count int64
	tx.Count(&count)

	if count == 0 {
		//不存在，新增
		return tx.Save(&obj)
	} else {
		//存在，修改
		upParam["updated_at"] = time.Now() //修改时间设置为当前
		return tx.Updates(upParam)
	}
}

// 根据类型，set值，目前写了几个常用类型，当类型不覆盖时，再添加新类型
func (c *Common[T]) setMap(m map[string]interface{}, f *schema.Field, v *reflect.Value) {
	if strings.EqualFold(string(f.DataType), "string") {
		m[f.DBName] = v.String()
	} else if strings.EqualFold(string(f.DataType), "uint") {
		m[f.DBName] = v.Uint()
	} else if strings.EqualFold(string(f.DataType), "int") {
		m[f.DBName] = v.Int()
	} else if strings.Contains(string(f.DataType), "float") {
		m[f.DBName] = v.Float()
	} else if strings.EqualFold(string(f.DataType), "time") {
		m[f.DBName] = v.Interface().(time.Time)
	} else {
		m[f.DBName] = v.String()
	}
}

func (c *Common[T]) FindAll(query map[string]interface{}) ([]T, error) {
	var model []T
	tx := c.model()
	for _, w := range c.CustomWhere {
		tx = tx.Where(w)
	}
	err := tx.Where(query).Preload(clause.Associations).Find(&model).Error
	return model, err
}

func (c *Common[T]) FindAllOrderBy(query map[string]interface{}, orderBy string) ([]T, error) {
	var model []T
	tx := c.model()
	for _, w := range c.CustomWhere {
		tx = tx.Where(w)
	}
	err := tx.Where(query).Order(orderBy).Preload(clause.Associations).Find(&model).Error
	return model, err
}

func (c *Common[T]) FindNoPreloadAll(query map[string]interface{}) ([]T, error) {
	var model []T
	tx := c.model()
	for _, w := range c.CustomWhere {
		tx = tx.Where(w)
	}
	err := tx.Where(query).Find(&model).Error
	return model, err
}

func (c *Common[T]) FindLeftJoinAll(query map[string]interface{}) ([]T, error) {
	var model []T
	tx := c.model()
	for _, w := range c.CustomWhere {
		tx = tx.Where(w)
	}
	for _, s := range c.LeftJoin {
		tx = tx.Joins(s)
	}
	err := tx.Where(query).Preload(clause.Associations).Find(&model).Error
	return model, err
}

type RawCountResult struct {
	Total int64
}

func (c *Common[T]) RawCount(sql, field string) (int64, error) {
	tx := c.model()
	var result RawCountResult
	err := tx.Raw(sql).Select(field).Scan(&result).Error
	return result.Total, err
}

func (c *Common[T]) RawList(sql string) ([]T, error) {
	tx := c.model()
	var result []T
	err := tx.Raw(sql).Scan(&result).Error
	return result, err
}

func (c *Common[T]) RawOne(sql string) (T, error) {
	sql += " limit 1 "
	tx := c.model()
	var result T
	err := tx.Raw(sql).Scan(&result).Error
	return result, err
}

func (c *Common[T]) DeleteByStringColumn(column, value string) *gorm.DB {
	var model T
	tx := c.model()
	querySql := column + " = '" + value + "'"
	return tx.Unscoped().Where(querySql).Delete(&model)
}

func (c *Common[T]) DeleteByIntColumn(column string, value uint) *gorm.DB {
	var model T
	tx := c.model()
	querySql := column + " = " + fmt.Sprintf("%d", value)
	return tx.Unscoped().Where(querySql).Delete(&model)
}

func (c *Common[T]) BatchDeleteByStringColumn(column string, values []string) *gorm.DB {
	var model T
	tx := c.model()
	for i, v := range values {
		values[i] = "'" + v + "'"
	}
	arr := []string{column, " IN (", strings.Join(values, ","), ")"}
	return tx.Unscoped().Where(strings.Join(arr, "")).Delete(&model)
}
